#include "threads.h"

// ==============================================================================
processThread::processThread(QObject *parent)
    : QThread(parent)
{
    processSubDirs = true;
    useNativeIcons = false;
}

void processThread::run()
{
    t_totalItemsCount = 0;
    t_totalFilesCount = 0;
    t_totalFoldersCount = 0;

    // process folder mode
    if ( run_mode == 2 ) // view mode changed
    {
        QTreeWidgetItemIterator iter1( mainWidget );
        while (*iter1)
        {
            if ( (*iter1)->text(30) != "ftp" )
                t_totalItemsCount++;
            ++iter1;
        }
        emit calculateFinished();

        redrawByViewMode();
    }
    else
    {
        // first calculate for progress
        for ( int i = 0; i < initialDirPathList.size(); i++ )
            calculateDir( initialDirPathList.at(i) );
        emit calculateFinished();

        if ( run_mode == 0 )
        {
            for ( int i = 0; i < initialDirPathList.size(); i++ )
                processDir ( initialDirPathList.at(i), rootItemList.at(i) );

            // second calculate for total files, folder status message
            countItems();
        }
        // refresh folder mode
        else if ( run_mode == 1 )
            refresh();
    }
}

void processThread::setRunMode( int mode )
{
    currStep = 0;
    deleted = 0;
    modified = 0;
    added = 0;

    t_totalItemsCount = 0;
    t_totalFilesCount = 0;
    t_totalFoldersCount = 0;

    run_mode = mode;
}

void processThread::clear()
{
    mainWidget = NULL;
    initialDirPathList.clear();
    rootItemList.clear();
}

void processThread::setFilters( bool subDirs, bool nativeIcons )
{
    processSubDirs = subDirs;
    useNativeIcons = nativeIcons;
}

void processThread::appendInfo( const QString& dirPath, QTreeWidgetItem * rootItem )
{
    initialDirPathList.append( dirPath );
    rootItemList.append( rootItem );
}

QFileInfoList processThread::getFileInfoList( QDir _dir )
{
    return _dir.entryInfoList( QDir::NoDotAndDotDot|QDir::AllDirs|QDir::Files|
                               QDir::Hidden|QDir::NoSymLinks, QDir::DirsFirst );
}

void processThread::calculateDir( const QString& dirPath )
{
    QDir tempDir( dirPath );
    t_totalItemsCount += tempDir.entryList( QDir::AllDirs|QDir::Files|
                                                          QDir::Hidden |
                                                          QDir::NoDotAndDotDot ).count();

    QFileInfoList innerFolderList = tempDir.entryInfoList(QDir::AllDirs|QDir::NoDotAndDotDot);
    for ( int i = 0; i < innerFolderList.size(); i++ )
        calculateDir( innerFolderList.at(i).absoluteFilePath() );
}

void processThread::countItems()
{
    QTreeWidgetItemIterator it( mainWidget );
    while (*it)
    {
        if ( isItemFolder(*it) )
            t_totalFoldersCount++;
        else
            t_totalFilesCount++;
        ++it;
    }
}

void processThread::processDir( const QString& dPath, QTreeWidgetItem * itt )
{
    QFileInfoList myList = getFileInfoList( QDir( dPath ) );

    if ( myList.size() > 0 )
    {
        for ( int i = 0; i < myList.size(); i++ )
        {
            QFileInfo lFileInfo = myList.at(i);
            // process Item
            QTreeWidgetItem * item;
            if ( !itt )
                item = new QTreeWidgetItem( mainWidget, 0 );
            else
                item = new QTreeWidgetItem( itt, 0 );

            drawTreeItem( item, lFileInfo.isDir(), lFileInfo );

            // step for progressBar
            currStep++;
            emit makeStep( currStep );

            // start recurcy if nesessary
            if ( lFileInfo.isDir() && processSubDirs )
                processDir( lFileInfo.absoluteFilePath(), item );
        }
    }
}

void processThread::drawTreeItem( QTreeWidgetItem * item, bool isFolder,
                               const QFileInfo &fInfo )
{
    int col = -1;
    QString dispFileName = fInfo.completeBaseName();
    if ( mySettings->displayFileExtension() )
    {
        if ( mySettings->attachToFilename() )
            dispFileName = fInfo.fileName();
        else
        {
            col = customColumnNumber( "File ext" );
            if ( col != -1 )
                item->setData( col, Qt::DisplayRole, fInfo.suffix() );
        }
    }
    item->setData( 0, Qt::DisplayRole, dispFileName );
    // 8.3 name format
    if ( !isFolder )
    {
        item->setData( 20, Qt::DisplayRole,
                       fInfo.completeBaseName().left(8) + "." + fInfo.suffix().left(3) );
    }
    else
        item->setData( 20, Qt::DisplayRole, dispFileName );

    // set Icons
    if ( useNativeIcons )
    {
        QFileIconProvider myIPro;
        QIcon icon = myIPro.icon(fInfo);
        item->setIcon ( 0, icon.pixmap(16) );
    }
    else
    {
        QPixmap pixmap( isFolder ? ":/images/folder.png" : ":/images/file.png");
        item->setIcon ( 0, pixmap );
    }

    col = customColumnNumber( "Type" );
    if ( !isFolder )
    {
        if ( col != -1 )
            item->setData( col, Qt::DisplayRole, "file" );
        item->setData( 22, Qt::DisplayRole, "file" );
    }
    else
    {
        if ( col != -1 )
            item->setData( col, Qt::DisplayRole, "folder" );
        item->setData( 22, Qt::DisplayRole, "folder" );
    }

    col = customColumnNumber( "Full path" );
    if ( col != -1 )
        item->setData( col, Qt::DisplayRole,
                       QDir::toNativeSeparators( fInfo.absoluteFilePath())
                       .replace( QDir::separator(), mySettings->getSeparator() ) );

    if ( !isFolder )
    {
        col = customColumnNumber( "Size" );
        if ( col != -1 )
            item->setData( col, Qt::DisplayRole, getConvertedSize( fInfo.size() ) );
    }

    col = customColumnNumber( "Creation date" );
    if ( col != -1 )
        item->setData( col, Qt::DisplayRole,
                       fInfo.created().toString( mySettings->getDateFormat() + " " + mySettings->getTimeFormat() ));

    col = customColumnNumber( "Modification date" );
    if ( col != -1 )
        item->setData( col, Qt::DisplayRole,
                       fInfo.lastModified().toString( mySettings->getDateFormat() + " " + mySettings->getTimeFormat() ));

    col = customColumnNumber( "Last access date" );
    if ( col != -1 )
        item->setData( col, Qt::DisplayRole,
                       fInfo.lastRead().toString( mySettings->getDateFormat() + " " + mySettings->getTimeFormat() ));

    if ( !isFolder )
    {
        col = customColumnNumber( "File attr" );
        if ( col != -1 )
            item->setData( col, Qt::DisplayRole, getFileAttr( fInfo.absoluteFilePath() ) );
    }

    col = customColumnNumber( "Owner" );
    if ( col != -1 )
        item->setData( col, Qt::DisplayRole, fInfo.owner() );

    if ( !isFolder )
    {
        col = customColumnNumber( "Md5 hash" );
        if ( col != -1 )
            item->setData( col, Qt::DisplayRole, getMd5Hash( fInfo.absoluteFilePath() ) );
    }

    if ( !isFolder )
    {
        item->setData( 23, Qt::DisplayRole, QString::number(fInfo.size()) );
        item->setData( 28, Qt::DisplayRole, getFileAttr( fInfo.absoluteFilePath() ) );
    }
    item->setData( 26, Qt::DisplayRole, fInfo.absoluteFilePath() );
    item->setData( 27, Qt::DisplayRole, "" );
    item->setData( 29, Qt::DisplayRole, fInfo.lastModified().toString( Qt::ISODate ) );
    item->setText( 30, "folder" );
}

bool processThread::isItemFolder( QTreeWidgetItem * item )
{
    if ( item->text(22) == "folder" )
        return true;
    return false;
}

int processThread::customColumnNumber( const QString& name )
{
    int resCol = -1;
    for (int i = 0; i < mainWidget->columnCount(); i++ )
    {
        if ( mainWidget->headerItem()->text(i).left( name.length() ) == name )
        {
            resCol = i;
            break;
        }
    }
    return resCol;
}

QString processThread::getConvertedSize( qint64 _size )
{
    if ( mySettings->displayFileSizeType() != 5 )
    {
        double ret = _size;
        for ( int it = 1; it < mySettings->displayFileSizeType(); it++ )
            ret = ret / 1024;

        QString suff;
        switch ( mySettings->displayFileSizeType() )
        {
        case 1:
            {
                suff = " bytes";
                break;
            }
        case 2:
            {
                suff = " kb";
                break;
            }
        case 3:
            {
                suff = " Mb";
                break;
            }
        case 4:
            {
                suff = " Gb";
                break;
            }
        }

        QString retStr = QString::number( ret, 'f', 2 );
        retStr.replace( retStr.mid( retStr.length()-3, 1 ), mySettings->getFractionDel() );

        int _count = (retStr.length()-3)/3+1;
        if ( _count == (retStr.length()-3)/3 )
            _count--;
        for (int i = 1; i < _count; i++)
            retStr.insert( (retStr.length()-3)-(3*i)-(i-1), mySettings->getGroupSeparator() );
        retStr += suff;
        retStr = retStr.simplified();
        if ( retStr.left(1) == mySettings->getGroupSeparator() )
            retStr.remove(0,1);
        return retStr;
    }
    else
    {
        double bytesCount = _size;

        int degree = -1;
        QString degreeStr = " bytes";
        while ( bytesCount >= 1 )
        {
            bytesCount = bytesCount / 1024.0;
            degree++;
        }

        bytesCount *= 1024.0;
        switch ( degree )
        {
        case 1:
            {
                degreeStr = " kb";
                break;
            }
        case 2:
            {
                degreeStr = " Mb";
                break;
            }
        case 3:
            {
                degreeStr = " Gb";
                break;
            }
        }
        QString retStr = QString::number( bytesCount, 'f', 2 );
        retStr.replace( retStr.mid( retStr.length()-3, 1 ), mySettings->getFractionDel() );

        int _count = (retStr.length()-3)/3+1;
        if ( _count == (retStr.length()-3)/3 )
            _count--;
        for (int i = 1; i < _count; i++)
            retStr.insert( (retStr.length()-3)-(3*i)-(i-1), mySettings->getGroupSeparator() );
        retStr += degreeStr;
        retStr = retStr.simplified();
        if ( retStr.left(1) == mySettings->getGroupSeparator() )
            retStr.remove(0,1);
        return retStr;
    }

    return QString::null;
}

QString processThread::getFileAttr( const QString& name )
{
    QString retStr;
    QFileInfo fileInfo(name);
    if ( fileInfo.isHidden() )
        retStr += "Hidden";
    if ( !fileInfo.isWritable() )
    {
        if ( !retStr.isEmpty() )
            retStr += "; ";
        retStr += "Read only";
    }

    LPCTSTR fileNameL = (LPCTSTR)name.utf16();
    DWORD dwAttrs = GetFileAttributes( fileNameL );
    if (dwAttrs != INVALID_FILE_ATTRIBUTES)
    {
        if ( dwAttrs & FILE_ATTRIBUTE_ARCHIVE )
        {
            if ( !retStr.isEmpty() )
                retStr += "; ";
            retStr += "Archive";
        }
        if ( dwAttrs & FILE_ATTRIBUTE_SYSTEM )
        {
            if ( !retStr.isEmpty() )
                retStr += "; ";
            retStr += "System";
        }
    }
    return retStr;
}

QString processThread::getMd5Hash( const QString& name )
{
    QFile prFile(name);
    if ( !prFile.open( QIODevice::ReadOnly ) )
        return QString::null;

    QByteArray hashFile = prFile.readAll();
    QByteArray md5data = QCryptographicHash::hash(hashFile, QCryptographicHash::Md5);

    prFile.close();

    return QString(md5data.toHex() );
}

void processThread::refresh()
{
    deleted = 0;
    modified = 0;
    added = 0;

    QTreeWidgetItemIterator it( mainWidget );
    while (*it)
    {
        if ( !(*it)->text(26).isEmpty() )
        {
            QFileInfo fInfo( (*it)->text(26) );
            if ( !fInfo.exists() && (*it)->text(30) != "ftp" )
            {
                deleted += getChildItemsCount(*it); //child Items
                deleted++; // this item
                if ( (*it)->parent() )
                    (*it)->parent()->takeChild( (*it)->parent()->indexOfChild( *it ));
                else
                    mainWidget->takeTopLevelItem( mainWidget->indexOfTopLevelItem( *it ) );

                currStep++;
                emit makeStep( currStep );
            }
            else
            {
                if ( (*it)->text(22) == "file" && (*it)->text(27) != "+" && (*it)->text(30) != "ftp" )
                {
                    if ( fInfo.lastModified().toString( Qt::ISODate ) != (*it)->text( 29 ) ) // 29 - modification date
                    {
                        modified++;
                        drawTreeItem( *it, false, fInfo );
                        (*it)->setText(27, "+");

                        for ( int i = 0; i < mainWidget->columnCount(); i++ )
                        {
                            (*it)->setForeground( i, QBrush( QColor(0, 0, 255) ) );
                            QFont font = (*it)->font(0);
                            font.setBold( true );
                            (*it)->setFont( i, font );
                        }

                        currStep++;
                        emit makeStep( currStep );
                    }
                }
            }
        }
        ++it;
    }

    for ( int i = 0; i < initialDirPathList.size(); i++ )
        checkAdded ( initialDirPathList.at(i), rootItemList.at(i) );
}

int processThread::getChildItemsCount( QTreeWidgetItem * parent_item )
{
    int retRes = 0;
    retRes += parent_item->childCount();
    for ( int i = 0; i < parent_item->childCount(); i++ )
    {
        if ( parent_item->child(i)->childCount() > 0 )
            retRes += getChildItemsCount( parent_item->child(i) );
    }
    return retRes;
}

QTreeWidgetItem* processThread::itemWith( const QString& path )
{
    QTreeWidgetItemIterator it( mainWidget );
    while (*it)
    {
        if ( (*it)->text(26) == path )
            return *it;
        ++it;
    }
    return NULL;
}

void processThread::checkAdded( const QString& path, QTreeWidgetItem * itt )
{
    QFileInfoList myList = getFileInfoList( QDir( path ) );

    if ( myList.count() > 0 )
    {
        for ( int i = 0; i < myList.count(); i++ )
        {
            QFileInfo lFileInfo = myList.at(i);

            QTreeWidgetItem * fInfoItem = itemWith( lFileInfo.absoluteFilePath() );
            if ( !fInfoItem )
            {
                if ( !itt )
                    fInfoItem = new QTreeWidgetItem( mainWidget, 0 );
                else
                    fInfoItem = new QTreeWidgetItem( itt, 0 );

                added++;
                drawTreeItem( fInfoItem, lFileInfo.isDir(), lFileInfo );

                for ( int i = 0; i < mainWidget->columnCount(); i++ )
                {
                    fInfoItem->setForeground( i, QBrush( QColor(0, 130, 0) ) );
                    QFont font = fInfoItem->font(0);
                    font.setBold( true );
                    fInfoItem->setFont( i, font );
                }

                if ( processSubDirs )
                {
                    currStep += 0.5;
                    emit makeStep( currStep );
                }
            }

            // start recurcy
            if ( lFileInfo.isDir() )
                if ( (fInfoItem && (!processSubDirs && fInfoItem->childCount() > 0)) ||
                     processSubDirs )
                    checkAdded( lFileInfo.absoluteFilePath(), fInfoItem );
        }
    }
}

void processThread::redrawByViewMode()
{
    if ( mainWidget )
    {
        QTreeWidgetItemIterator iter1( mainWidget );
        while (*iter1)
        {
            if ( (*iter1)->text(30) == "folder" )
            {
                QFileInfo fileInfo( (*iter1)->text(26) );

                for (int i = 0; i < 31; i++)
                    (*iter1)->setText( i, QString::null );
                drawTreeItem( *iter1, fileInfo.isDir(), fileInfo );

                // step for progressBar
                currStep++;
                emit makeStep( currStep );
            }
            ++iter1;
        }
    }
}
